#include "sysclientsocket.h"
#include "client-socket.h"
#include "commonfunc.h"
#include <QDebug>
#include "cgproto.pb.h"
#include "base.pb.h"

CNS_INIT(ClientSocketProc)
//CNS_REGISTER_CNS_COM_FACTORY(ClientSocketProc)
CNS_REGISTER_CNS_COM_FACTORY2(ClientSocketProc, CnsComponent)
CNS_INIT_END

SysClientSocket::SysClientSocket()
{

}

void SysClientSocket::_on_disconnect(uv_handle_t* handle)
{
    // TODO: This is a dummy example

    ClientSocketProc* client = CAST_REF_TYPE_FROM_VOID(ClientSocketProc, handle->data);

    free(client);
}

void SysClientSocket::f__on_connect(CnsComponentPtr com, uv_connect_t* req, int status)
{
    ClientSocketProcPtr clientSocketProcPtr = com.staticCast<ClientSocketProc>();
    client_read(clientSocketProcPtr);
}

void SysClientSocket::_on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
    ClientSocketProc* client = CAST_REF_TYPE_FROM_VOID(ClientSocketProc, stream->data);

    if (nread == 0) {
        return;
    }

    if (nread < 0) {
        qDebug() << QString("disconnect server %1:%2!").arg(client->getIp()).arg(client->getPort());
        client_disconnect(client, _on_disconnect);
        return;
    }

    long long count = nread;
    char *package = buf->base;
    BaseProto &proto = client->getBaseProto();
    uint32_t handleSize;

    do{

        char *msgBuf = proto.addToReceiveBuf(handleSize, package, static_cast<uint32_t>(count));

        if(msgBuf){

            cgproto::Message msg;

            if(proto.decode(msgBuf, msg))
            {
                qDebug() << QString("Read server %1:%2 response: %3 len: %4").arg(client->getIp()).arg(client->getPort()).arg(msg.type()).arg(nread);
                const QMap<QVariant, QVariant>& dispatcher = client->property("dispatcher").value<QMap<QVariant, QVariant>>();
                CnsComponentPtr p = dispatcher[msg.type()].value<CnsComponentPtr>();
                p->exec("_on_read", Q_ARG(cgproto::Message&, msg));
            } else{
                qDebug() << QString("Read server %1:%2 can't decode package len: %3").arg(client->getIp()).arg(client->getPort()).arg(nread);
            }
        } else break;
        count -= handleSize;
        package += handleSize;
    } while(count > 0);
}

void SysClientSocket::client_read(ClientSocketProcPtr client)
{
    c_cns::ref(&client->getSocket().data, client.data());
    uv_read_start(reinterpret_cast<uv_stream_t*>(&client->getSocket()), _on_alloc_buffer, _on_read);
}

void SysClientSocket::f_run(CnsComponentPtr com, uv_loop_t *loop)
{
    ClientSocketProc* clientSocketProc = static_cast<ClientSocketProc*>(com.data());
    struct sockaddr_in dest;
    clientSocketProc->setLoop(loop);
    clientSocketProc->getAsync()->data = loop;

    uv_async_init(loop, clientSocketProc->getAsync(), uv_async_stop_loop);

    uv_tcp_init(loop, &clientSocketProc->getSocket());
    c_cns::ref(&clientSocketProc->getSocket().data, clientSocketProc);

    uv_ip4_addr(clientSocketProc->getIp().toLatin1(), clientSocketProc->getPort(), &dest);
    uv_tcp_connect(&clientSocketProc->getConnect(), &clientSocketProc->getSocket(), reinterpret_cast<const struct sockaddr*>(&dest), [](uv_connect_t* req, int status)
    ->void {
        if(!status){
            uv_stream_t* stream;

            stream = req->handle;
            ClientSocketProc *client = CAST_REF_TYPE_FROM_VOID(ClientSocketProc, stream->data);
            client->exec("_on_connect", Q_ARG(uv_connect_t*, req), QStringList(), Q_ARG(int, status));
        }
    });

}

void SysClientSocket::client_disconnect(ClientSocketProc* client, uv_close_cb callback)
{
    uv_close(reinterpret_cast<uv_handle_t*>(&client->getSocket()), callback);
}

void SysClientSocket::_on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
    ClientSocketProc *client = CAST_REF_TYPE_FROM_VOID(ClientSocketProc, handle->data);

    buf->base = client->getBaseProto().getBuf();
    buf->len = client->getBaseProto().getSize();
}
